////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include "Test_PathfindingGame.hpp"

#include <sstream>

#include <SFML/Graphics.hpp>

#include <SmegCore/Game/DataManager.hpp>
#include <SmegCore/Entity/EntityManager.hpp>

#include <SmegAI/Movement/Pathfinding/Algorithms/AStar.hpp>
#include <SmegAI/Movement/Pathfinding/Algorithms/JumpPointSearch.hpp>

////////////////////////////////////////////////////////////
/// Defines
////////////////////////////////////////////////////////////
#define LADYBUG_NEXT_STEP_THRESHOLD 5.0f

////////////////////////////////////////////////////////////
/// Constructor
////////////////////////////////////////////////////////////
Test_PathfindingGame::Test_PathfindingGame() :
    GameBase(),
    m_pFont( NULL ),
    m_pHelp( NULL ),
    m_pPathfindingInfo( NULL ),
    m_bShowHelp( true ),
    m_EndPoint(),
    m_bEndChanged( true ),
    m_pBackgroundTex( NULL ),
    m_pBackground( NULL ),
    m_pAIGrid( NULL ),
    m_bStepByStep( false ),
    m_PathfindingType( eType_None ),
    m_pCurrentAlgorithm( NULL ),
    m_pAStar( NULL ),
    m_pJumpPoint( NULL )
{
}

////////////////////////////////////////////////////////////
/// Destructor
////////////////////////////////////////////////////////////
Test_PathfindingGame::~Test_PathfindingGame()
{
}

////////////////////////////////////////////////////////////
/// Init the game
////////////////////////////////////////////////////////////
void Test_PathfindingGame::Init( sf::VideoMode _Mode, const char* _pTitle, sf::Uint32 _Style, const sf::ContextSettings& _Settings ) {
    GameBase::Init( _Mode, _pTitle, _Style, _Settings );

    // // Init the images
    smeg::core::DataManager::GetInstance()->AddTexture( "Ladybug", "data/Ladybug.png" );
    
    // // Help
    m_pFont = new sf::Font();
    if ( m_pFont->loadFromFile( "data/fonts/COUR.ttf" ) ) {
        m_pHelp = new sf::Text("Help :\n" \
                        " - F1 : Display/Hide this help\n" \
                        " - F3 : NO Pathfinding\n" \
                        " - F4 : ALL Pathfindings\n" \
                        " - F5 : AStar\n" \
                        " - F6 : JPS\n" \
                        " - F9 : Add Ladybug\n" \
                        " - F10 : Remove Ladybug\n" \
                        " - F11 : Toggle Step by Step\n",
                        *m_pFont,
                        12);
        m_pHelp->setColor( sf::Color::White );

        m_pPathfindingInfo = new sf::Text("",
                        *m_pFont,
                        12);
        m_pPathfindingInfo->setColor( sf::Color::White );
        m_pPathfindingInfo->setPosition( _Mode.width - 200.0f, 0.0f );
    }

    // // Misc
    // Background
    m_pBackgroundTex = new sf::Texture();
    if ( m_pBackgroundTex->loadFromFile( "data/Background.png" ) ) {
        m_pBackground = new sf::Sprite(*m_pBackgroundTex);
    }
    
    // // Pathfinding
    m_pAIGrid = new smeg::ai::Grid( _Mode.width, _Mode.height );
    m_pAIGrid->LoadFromImage( "data/PathMap.png" );

    m_pAStar = new smeg::ai::AStar( m_pAIGrid );
    m_pJumpPoint = new smeg::ai::JumpPointSearch( m_pAIGrid );

    m_LadyBugs.push_back( new smeg::ai::AIEntity( m_pRenderWindow, "Ladybug" ) );
    m_LadyBugs[0]->SetCenter( 50.0f, _Mode.height - 50.0f );
    m_LadyBugs[0]->SetNextStepThreshold( LADYBUG_NEXT_STEP_THRESHOLD );
}

////////////////////////////////////////////////////////////
/// DeInit the game
////////////////////////////////////////////////////////////
void Test_PathfindingGame::DeInit() {
    delete m_pFont;
    m_pFont = NULL;
    delete m_pHelp;
    m_pHelp = NULL;
    delete m_pPathfindingInfo;
    m_pPathfindingInfo = NULL;

    delete m_pBackgroundTex;
    m_pBackgroundTex = NULL;
    delete m_pBackground;
    m_pBackground = NULL;
    
    delete m_pAIGrid;
    m_pAIGrid = NULL;
    m_pCurrentAlgorithm = NULL;

    delete m_pAStar;
    m_pAStar = NULL;
    delete m_pJumpPoint;
    m_pJumpPoint = NULL;

    m_LadyBugs.clear();

    smeg::core::EntityManager::GetInstance()->ClearScene();

    GameBase::DeInit();
}

////////////////////////////////////////////////////////////
/// Manage an incoming event
////////////////////////////////////////////////////////////
void Test_PathfindingGame::ManageEvent( const sf::Event& _Event ) {
    GameBase::ManageEvent( _Event );

    switch ( _Event.type ) {
    case sf::Event::KeyPressed:
        switch (_Event.key.code) {
        case sf::Keyboard::F1:
            m_bShowHelp = !m_bShowHelp;
            break;
            
        case sf::Keyboard::F3:
            ClearAllPathfinding();
            m_PathfindingType = eType_None;
            break;
        case sf::Keyboard::F4:
            ClearAllPathfinding();
            m_PathfindingType = eType_All;

            m_bEndChanged = true;
            break;

        case sf::Keyboard::F5:
            ClearAllPathfinding();

            if ( m_bStepByStep ) {
                if ( m_pAStar->DebugStartPath( m_LadyBugs[0]->GetCenter(), m_EndPoint ) ) {
                    m_PathfindingType = eType_AStar;
                    m_pCurrentAlgorithm = m_pAStar;
                }
            } else {
                m_PathfindingType = eType_AStar;
            }

            m_bEndChanged = true;
            break;
        case sf::Keyboard::F6:
            ClearAllPathfinding();

            if ( m_bStepByStep ) {
                if ( m_pJumpPoint->DebugStartPath( m_LadyBugs[0]->GetCenter(), m_EndPoint ) ) {
                    m_PathfindingType = eType_JPS;
                    m_pCurrentAlgorithm = m_pJumpPoint;
                }
            } else {
                m_PathfindingType = eType_JPS;
            }

            m_bEndChanged = true;
            break;

        case sf::Keyboard::F9:
            m_LadyBugs.push_back( new smeg::ai::AIEntity( m_pRenderWindow, "Ladybug", m_LadyBugs.back()->GetCenter() ) );
            m_LadyBugs.back()->SetNextStepThreshold( LADYBUG_NEXT_STEP_THRESHOLD );
            break;
        case sf::Keyboard::F10:
            if ( m_LadyBugs.size() > 1 ) {
                delete m_LadyBugs.back();
                m_LadyBugs.pop_back();
            }
            break;
        case sf::Keyboard::F11:
            ClearAllPathfinding();
            m_bStepByStep = !m_bStepByStep;
            break;
        }
        break;

    case sf::Event::MouseButtonPressed:
        ClearAllPathfinding();

        m_EndPoint.x = (float)_Event.mouseButton.x;
        m_EndPoint.y = (float)_Event.mouseButton.y;

        m_bEndChanged = true;
        break;
    }
}

////////////////////////////////////////////////////////////
/// Call all the Update in the other systems
////////////////////////////////////////////////////////////
void Test_PathfindingGame::Update() {
    GameBase::Update();

    float fDeltaTime = m_LastFrameTime.asSeconds();
    if ( m_PathfindingType != eType_None ) {
        if ( m_bStepByStep ) {
            if ( m_PathfindingType & eType_AStar ) {
                if ( m_pAStar->DebugStepPath( m_LadyBugs[0]->GetCenter(), m_EndPoint ) ) {
                    m_PathfindingType = ePathfindingType(m_PathfindingType ^ eType_AStar);
                }
            }
            if ( m_PathfindingType & eType_JPS ) {
                if ( m_pJumpPoint->DebugStepPath( m_LadyBugs[0]->GetCenter(), m_EndPoint ) ) {
                    m_PathfindingType = ePathfindingType(m_PathfindingType ^ eType_JPS);
                }
            }
        } else {
            // AStar
            if ( m_PathfindingType & eType_AStar && m_bEndChanged ) {
                m_PathfindingClock.restart();
                for ( unsigned i = 0; i < m_LadyBugs.size(); ++i ) {
                    m_LadyBugs[i]->FollowPath( m_pAStar->FindPath( m_LadyBugs[i]->GetCenter(), m_EndPoint ) );
                }
                m_AStarTime = m_PathfindingClock.getElapsedTime();
            }
    
            // JPS
            if ( m_PathfindingType & eType_JPS && m_bEndChanged ) {
                m_PathfindingClock.restart();
                for ( unsigned i = 0; i < m_LadyBugs.size(); ++i ) {
                    m_LadyBugs[i]->FollowPath( m_pJumpPoint->FindPath( m_LadyBugs[i]->GetCenter(), m_EndPoint ) );
                }
                m_JPSTime = m_PathfindingClock.getElapsedTime();
            }

            for ( unsigned i = 0; i < m_LadyBugs.size(); ++i ) {
                m_LadyBugs[i]->Update( fDeltaTime );
            }
        }
    }

    m_bEndChanged = false;
}

////////////////////////////////////////////////////////////
/// Call all the Render in the other systems
////////////////////////////////////////////////////////////
void Test_PathfindingGame::Render() {
    m_pRenderWindow->draw( *m_pBackground );

    m_pAIGrid->DebugRender( m_pRenderWindow );

    for ( unsigned i = 0; i < m_LadyBugs.size(); ++i ) {
        m_LadyBugs[i]->Render();
        m_LadyBugs[i]->DebugRender();
    }

    if ( m_pCurrentAlgorithm ) {
        m_pCurrentAlgorithm->DebugRenderFindPath( m_pRenderWindow );
    }

    if ( m_bStepByStep ) {
        m_pPathfindingInfo->setString( "Step by Step" );
    } else if ( m_PathfindingType != eType_None ) {
        std::stringstream infoStr;
        infoStr << "Pathfinding Info :" << std::endl;
        if ( m_PathfindingType & eType_AStar ) {
            infoStr << "- AStar : " << m_AStarTime.asSeconds() << "s" << std::endl;
        }
        if ( m_PathfindingType & eType_JPS ) {
            infoStr << "- JPS : " << m_JPSTime.asSeconds() << "s" << std::endl;
        }
        if ( eType_All == m_PathfindingType ) {
            if ( m_AStarTime < m_JPSTime ) {
                infoStr << "- JPS = " << ( m_JPSTime.asSeconds() / m_AStarTime.asSeconds() ) << "* AStar" << std::endl;
            } else if ( m_AStarTime > m_JPSTime ) {
                infoStr << "- AStar = " << ( m_AStarTime.asSeconds() / m_JPSTime.asSeconds() ) << "* JPS" << std::endl;
            } else {
                infoStr << "- AStar == JPS" << std::endl;
            }
        }

        m_pPathfindingInfo->setString( infoStr.str() );
    }

    m_pRenderWindow->draw( *m_pPathfindingInfo );

    DisplayHelp();
}

////////////////////////////////////////////////////////////
/// Show/Hide the help
////////////////////////////////////////////////////////////
void Test_PathfindingGame::DisplayHelp() {
    if ( m_bShowHelp ) {    
        m_pRenderWindow->draw( *m_pHelp );
    }
}

////////////////////////////////////////////////////////////
/// Clear all the pathfinding algorithms
////////////////////////////////////////////////////////////
void Test_PathfindingGame::ClearAllPathfinding() {
    m_pAStar->ClearPath();
    m_pJumpPoint->ClearPath();
    
    m_AStarTime = sf::Time::Zero;
    m_JPSTime   = sf::Time::Zero;

    m_PathfindingType = eType_None;
    m_pCurrentAlgorithm = NULL;

    for ( unsigned i = 0; i < m_LadyBugs.size(); ++i ) {
        m_LadyBugs[i]->SetVelocity( 0.0f, 0.0f );
    }
}